package inglar.autotc.server.daemon;

import inglar.autotc.server.ConfigFile;
import inglar.autotc.server.Host;
import inglar.autotc.shared.Values;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * A simple Linux service that sends the results of the script: <br><br>{@code netstat -Wn -tu}
 * @author Lee
 */
public class DaemonClient implements Runnable {
	
	private static ConfigFile cfg = null;
	
	private static String errfile = null;
	private static String outfile = null;
	private static String pidfile = null;
	private static String jarfile = null;
	private static String entrypt = null;
	
	private static String cfgport = Values.CLIENT_DEFAULT_PORT;
	
	public static void main(String[] args) {
		
		// must have JAVA_HOME environment variable set
		String javaHome = Host.getJavaHome();
		if(javaHome == null) Host.fatal(Values.ErrorCode.D006javahome404);
		
		cfg = new ConfigFile("client.cfg");
		
		errfile = cfg.getValue("ERRFILE", "autotc-client-error.log");
		outfile = cfg.getValue("OUTFILE", "autotc-client.log");
		pidfile = cfg.getValue("PIDFILE", "autotc-client.jsvc.pid"); 
		jarfile = cfg.getValue("JARFILE", "autotc-client.jar");
		entrypt = cfg.getValue("ENTRYPT", DaemonClient.class.getName());
		cfgport = cfg.getValue("PORT", Values.CLIENT_DEFAULT_PORT + "");
		
		if(args.length == 0) {
			Host.out(Values.CLIENT_CMDHELP);
		} else if(args[0].equals("-start")) {
			
			try {
				String r = "";
				
				// check if the client daemon is still running
				File file = new File(pidfile);
				if(file.exists() && file.canRead()) {
					r = Host.exec("cat " + pidfile);
					Host.fatal("The daemon is still running. PID: " + r + " (" + pidfile + ")");
				}
				
				// delete outfile and err file before starting
				Host.exec("rm -f " + errfile + "; rm -f " + outfile);
				
				// execute jsvc 
				r = Host.exec(
						"sudo jsvc" +
						" -errfile " + errfile +
						" -outfile " + outfile +
						" -pidfile " + pidfile +
						" -home " + javaHome +
						" -cp " +  jarfile +
						" -D" + Values.ENVNAME_JAVA_CLIENT_PORT + "=" + cfgport +
						" " + entrypt
						);
				if(!r.isEmpty()) {
					Host.fatal("Unexpected String object returned: '" + r + "'");
				}
				
				Host.out("Starting . . .");
				
				boolean hasStarted = false;
				for(int i = 0; i < 5; i++) { // five tries
					Thread.sleep(2500);
					if(file.exists()) {
						hasStarted = true;
						break;
					}
					Host.out(".");
				}
				if(hasStarted) {
					Host.out("The daemon has started. Please see " + outfile + " and " + errfile + " for details.");
				} else {
					Host.out("The daemon did not start. Please see " + outfile + " and " + errfile + " for details.");
				}
			} catch (Throwable t) {
				Host.throwableDump(t);
			} 
		} else if(args[0].equals("-stop")) {
			
			try {
				String r = Host.exec(
						"sudo jsvc" +
//						" -errfile " + cfg.getErrFile() +
//						" -outfile " + cfg.getOutFile() +
						" -pidfile " + pidfile +
						" -home " + javaHome +
//						" -cp " +  cfg.getAppJar() +
						" -stop" +
						" " + entrypt
						);
				if(!r.isEmpty()) {
					throw new Exception(r);
				}
			} catch (Throwable t) {
				Host.throwableDump(t);
			}
			
		} else if(args[0].equals("-test")){
			
			Host.out(Host.getJavaHome());
			
		} else {
			Host.out(Values.CLIENT_CMDHELP);
		}
	}
	
/* ========================================================================== */
	
	
	private Thread thread = null;
	private boolean ALIVE = true; 
	private boolean RUNSTOP = true;
	
	private String port = Values.CLIENT_DEFAULT_PORT;
	
	private ServerSocket serverSocket = null;
	
	public void doConnect() {
		try {
			while(true) {
				System.out.println("waiting for connection . . .");	
				Socket conn = serverSocket.accept();
				System.out.println("connection eccepted from " + conn.getLocalAddress().getHostAddress());
				runConnect(conn);
			}
		} catch (IOException ioe) {
			System.err.println("IOE at port " + this.port);
			ioe.printStackTrace();
		}
	}
	
	public void runConnect(Socket conn) {
		class ConnectThread implements Runnable {
			Socket socket = null;
			
			public ConnectThread(Socket conn) {
				this.socket = conn;
			}

			@Override
			public void run() {
				
				try {
					BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
					OutputStream out = socket.getOutputStream();
					PrintWriter wout = new PrintWriter(out, true);
					
					String inret = "";
					
					// wait for handshake
					do {
						inret = in.readLine().trim();
					} while(!inret.equals("handshake"));
					
					// get netstat -Wn -tu
					String ret = Host.exec("netstat -Wn -tu");
					
//					// old implementation of writing ret to socket 
//					// we have to use this (see https://forums.oracle.com/forums/thread.jspa?threadID=1143667)
//					byte[] literalBuffer = { 0, 0, 0, 0, 0, 0, 0, 0 };
//					int charsLength = ret.length();
//					int charsRead = 0;
//					while(charsRead < charsLength) {
//						// copy a section of ret
//						for(int i = 0; i < literalBuffer.length; i++) {
//							literalBuffer[charsRead] = (byte) ret.charAt(i);
//							charsRead++;
//						}
//						// write it to stream
//						out.write(literalBuffer);
//					}
//					// send the stream contents
//					out.flush();
					
					wout.println(ret);
					
				} catch (IOException ioe) {
					
				} catch (Exception e) {
					
				} finally {
					// close the socket
					try {
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				} 
				
			} // end run
		} // end class
		
		Thread connect = new Thread(new ConnectThread(conn));
		connect.start();
	}
	
	@Override
	public void run() {
		
		while(ALIVE) {
			
			while(RUNSTOP); // do not do anything until start() is called
			
			if(serverSocket == null) break;
			
			doConnect();
			
		}
		
	}
	
	/**
	 * Called when this daemon is initialised.
	 * @param args
	 */
	public void init(String[] args) { 
		
		// get client port
		port = System.getProperty(Values.ENVNAME_JAVA_CLIENT_PORT, Values.CLIENT_DEFAULT_PORT);
		System.out.println(Values.ENVNAME_JAVA_CLIENT_PORT + ": " + port);
		
		Integer v = null;
		try { v = new Integer(port); } catch (Throwable cause) { cause.printStackTrace(); }
		if(v == null) v = new Integer(Values.CLIENT_DEFAULT_PORT);
		
		thread = new Thread(this);
		try {
			serverSocket = new ServerSocket(v.intValue());
		} catch (IOException e) {
			Host.fatal("Unable to create socket at port " + v.intValue());
			e.printStackTrace();
		}
	}
	
	/**
	 * Called when this daemon is started.
	 */
	public void start() {
		thread.start();
		
		System.out.println("started autotc daemon at " + Host.timeStamp());
		
		RUNSTOP = false;
	}
	
	/**
	 * Called when the daemon is stopped.
	 */
	public void stop() {
		this.ALIVE = false;
		
		try {
			serverSocket.close();
		} catch (IOException e) {
//			e.printStackTrace();
		}
		
		System.out.println("stopping autotc-client daemon... ");
	}
	
	
	/**
	 * Called when the daemon is destroyed.
	 */
	public void destroy() {
		thread = null;
		serverSocket = null;
		
		System.out.println("stopped autotc-client daemon at " + Host.timeStamp());
	}
}
